<!--
 * @Descripttion: 
 * @version: 
 * @Author: lihk
 * @Date: 2023-10-13 22:28:37
 * @LastEditors: lihk
 * @LastEditTime: 2023-10-16 23:23:46
-->
<script setup>
import * as  THREE from 'three'
import gsap from 'gsap';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';
import { Water } from 'three/examples/jsm/objects/Water2';
import { ref } from 'vue';
// 初始化场景
const scene = new THREE.Scene()
// 初始化相机
const camera = new THREE.PerspectiveCamera(
  60,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
)
//设置相机的位置
camera.position.set(-3.23, 2.98, 20)
// 修改宽高更新投影矩阵
camera.updateProjectionMatrix()

// 初始化渲染器
const renderer = new THREE.WebGL1Renderer({
  // 设置抗锯齿
  antialias: true,

})
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
// 设置电影调光色调映射
renderer.toneMapping = THREE.sRGBEncoding
renderer.toneMapping = THREE.ACESFilmicToneMapping
renderer.toneMappingExposure = 0.5
// 允许阴影
renderer.shadowMap.enabled = true
// 物理光照模型
renderer.physicallyCorrectLights = true

// 初始化控制器
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true

// 加载环境纹理
let rgbeLoader = new RGBELoader()
rgbeLoader.load('./textures/sky.hdr', (texture) => {
  texture.mapping = THREE.EquirectangularReflectionMapping
  scene.background = texture
  scene.environment = texture
})
// 设置水面效果
const waterGeometry = new THREE.CircleGeometry(300, 32)
const water = new Water(waterGeometry, {
  textureWidth: 1024,
  textureHeight: 1024,
  color: 0xeeeeff,
  flowDirection: new THREE.Vector2(1, 1),
  scale: 100,
})
scene.add(water)
water.rotation.x = -Math.PI / 2

//模型加载
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('./draco/')
const gltfLoader = new GLTFLoader()
gltfLoader.setDRACOLoader(dracoLoader)

// 加载模型
gltfLoader.load('./model/scene.glb',
  (gltf) => {
    let model = gltf.scene
    model.traverse((child) => {
      if (child.name === 'Plane') {
        child.visible = false
      }
      // 允许物体可以接受阴影
      if (child.isMesh) {
        child.castShadow = true
        child.receiveShadow = true
      }
    })
    scene.add(gltf.scene)
  }
)
// 添加聚光灯
const spotLight = new THREE.DirectionalLight(0xffffff, 1)
spotLight.position.set(0, 50, 0)

scene.add(spotLight)

// 添加点光源
const pointLight = new THREE.PointLight(0xffffff, 50)
pointLight.position.set(0.1, 2.4, 0)
pointLight.castShadow = true
scene.add(pointLight)

// 创建点光源组
const pointLightGroup = new THREE.Group()
pointLightGroup.position.set(-8, 2.5, -1.5)
let pointLightArr = []

const pointlight = new THREE.PointLight(0xffffff, 10);
let radius = 3
for (let i = 0; i < 3; i++) {
  // 创建球体当发光源
  const geometry = new THREE.SphereGeometry(0.2, 32, 32);
  const material = new THREE.MeshBasicMaterial({ color: 0xffffff });
  const sphere = new THREE.Mesh(geometry, material);
  pointLightArr.push(sphere)
  sphere.add(pointlight)
  pointLightGroup.add(sphere)
  sphere.position.set(
    radius * Math.cos((2 * i * Math.PI) / 3),
    Math.cos((2 * i * Math.PI) / 3),
    radius * Math.sin((2 * i * Math.PI) / 3),
  )
}

scene.add(pointLightGroup)

// 补间函数从0到2Π，是灯泡旋转
let options = {
  angle: 0,
}
gsap.to(options, {
  angle: Math.PI * 2,
  duration: 5,
  repeat: -1,
  ease: 'linear',
  onUpdate: () => {
    pointLightGroup.rotation.y = options.angle,
      // 让球上下动
      pointLightArr.forEach((item, index) => {
        item.position.set(
          radius * Math.cos((2 * index * Math.PI) / 3),
          Math.cos((2 * index * Math.PI) / 3 + options.angle * 5),
          radius * Math.sin((2 * index * Math.PI) / 3),
        )
      })
  }
})


function render () {
  requestAnimationFrame(render)
  renderer.render(scene, camera)
  controls.update()
}
render()

let scenes = [{
  text: '小朋友，圣诞节快乐！！！',
  callback: () => {
    //执行函数切换位置
    translateCamera(
      new THREE.Vector3(-3.23, 3, 20),
      new THREE.Vector3(-8, 2, 0)
    );
  }
}, {
  text: '很高兴可以认识到你哦',
  callback: () => {
    //执行函数切换位置
    translateCamera(new THREE.Vector3(7, 5, 23), new THREE.Vector3(0, 0, 0));
  }
}, {
  text: '多拍点照片，我爱看，啊哈哈哈哈',
  callback: () => {
    //执行函数切换位置
    translateCamera(new THREE.Vector3(10, 3, 0), new THREE.Vector3(5, 2, 0));
  }
}, {
  text: "快看，天上的星星，自己摘吧，啊哈哈哈",
  callback: () => {
    // 执行函数切
    translateCamera(new THREE.Vector3(-7, 10, 43), new THREE.Vector3(0, 0, 0));
    makeHeart();
  },
},
{
  text: "每一天都要开心哦",
  callback: () => {
    // 执行函数切
    translateCamera(
      new THREE.Vector3(-20, 1.3, 6.6),
      new THREE.Vector3(5, 2, 0)
    );
  },
},]
let index = ref(0)
// 使用补间动画移动相机
let timeLine1 = gsap.timeline();
let timeline2 = gsap.timeline();
// 定义相机移动函数
function translateCamera (position, target) {
  timeLine1.to(camera.position, {
    x: position.x,
    y: position.y,
    z: position.z,
    duration: 1,
    ease: "power2.inOut",
  });

  timeline2.to(controls.target, {
    x: target.x,
    y: target.y,
    z: target.z,
    duration: 1,
    ease: "power2.inOut",
  });
}
let isAnimate = false;
// 监听鼠标滚轮事件
window.addEventListener(
  "wheel",
  (e) => {
    // 在动画就返回，确保执行一次
    if (isAnimate) return;
    isAnimate = true;
    if (e.deltaY > 0) {
      index.value++;
      if (index.value > scenes.length - 1) {
        index.value = 0;
        restoreHeart();
      }
    }
    scenes[index.value].callback();
    setTimeout(() => {
      isAnimate = false;
    }, 1000);
  },
  false
);

// 实例化创建漫天星星
let starsInstance = new THREE.InstancedMesh(
  new THREE.SphereGeometry(0.1, 32, 32),
  new THREE.MeshStandardMaterial({
    color: 0xffffff,
    emissive: 0xffffff,
    emissiveIntensity: 10,
  }),
  100
);
// 星星随机到天上
let starsArr = [];
let endArr = [];
for (let i = 0; i < 100; i++) {
  let x = Math.random() * 100 - 50;
  let y = Math.random() * 100 - 50;
  let z = Math.random() * 100 - 50;
  starsArr.push(new THREE.Vector3(x, y, z));

  let matrix = new THREE.Matrix4();
  matrix.setPosition(x, y, z);
  starsInstance.setMatrixAt(i, matrix);
}
scene.add(starsInstance);

// 生成爱心
const x = 0, y = 60;
const radiu = 65;
const c = 0.551915024496 * radiu;

const heartShape = new THREE.Shape()
  .moveTo(x, y + radiu / 3)
  .bezierCurveTo(x + c, y + radiu, x + radiu, y + c, x + radiu, y)
  .bezierCurveTo(x + radiu, y - c, x + c, y - radiu / 2, x, y - radiu)
  .bezierCurveTo(x - c, y - radiu / 2, x - radiu, y - c, x - radiu, y)
  .bezierCurveTo(x - radiu, y + c, x - c, y + radiu, x, y + radiu / 3)

// 根据爱心路径获取点
let center = new THREE.Vector3(0, 2, 10);
for (let i = 0; i < 100; i++) {
  let point = heartShape.getPoint(i / 100);
  endArr.push(
    new THREE.Vector3(
      point.x * 0.1 + center.x,
      point.y * 0.1 + center.y,
      center.z
    )
  );
}
function makeHeart () {
  let params = {
    time: 0,
  };

  gsap.to(params, {
    time: 1,
    duration: 1,
    onUpdate: () => {
      for (let i = 0; i < 100; i++) {
        let x = starsArr[i].x + (endArr[i].x - starsArr[i].x) * params.time;
        let y = starsArr[i].y + (endArr[i].y - starsArr[i].y) * params.time;
        let z = starsArr[i].z + (endArr[i].z - starsArr[i].z) * params.time;
        let matrix = new THREE.Matrix4();
        matrix.setPosition(x, y, z);
        starsInstance.setMatrixAt(i, matrix);
      }
      starsInstance.instanceMatrix.needsUpdate = true;
    },
  });
}
function restoreHeart () {
  let params = {
    time: 0,
  };

  gsap.to(params, {
    time: 1,
    duration: 1,
    onUpdate: () => {
      for (let i = 0; i < 100; i++) {
        let x = endArr[i].x + (starsArr[i].x - endArr[i].x) * params.time;
        let y = endArr[i].y + (starsArr[i].y - endArr[i].y) * params.time;
        let z = endArr[i].z + (starsArr[i].z - endArr[i].z) * params.time;
        let matrix = new THREE.Matrix4();
        matrix.setPosition(x, y, z);
        starsInstance.setMatrixAt(i, matrix);
      }
      starsInstance.instanceMatrix.needsUpdate = true;
    },
  });
}
</script>

<template>
  <div class="scenes" style="
      position: fixed;
      left: 0;
      top: 0;
      z-index: 10;
      pointer-events: none;
      transition: all 1s;
    " :style="{
      transform: `translate3d(0, ${-index * 100}vh, 0)`,
    }">
    <div v-for="item in scenes" style="width: 100vw; height: 100vh" :key="item.text">
      <h1 style="padding: 100px 50px; font-size: 50px; color: #fff">
        {{ item.text }}
      </h1>
    </div>
  </div>
</template>

<style scoped>
* {
  margin: 0;
  padding: 0;
}

canvas {
  width: 100vw;
  height: 100vh;
  position: fixed;
  left: 0;
  top: 0;
}
</style>
